#include "server_socket.h"
#include "socket.h"
#include "inet_address.h"
#include <thread>
#include <iostream>


ServerSocket::ServerSocket(const InetAddress& bindAddr, int backlog) {
    sockfd_ = ::socket(bindAddr.family(), SOCK_STREAM, IPPROTO_TCP);
    assert(sockfd_ != invalid_socket);
    bind(bindAddr, backlog);
}

void ServerSocket::bind(const InetAddress& bindAddr, int backlog) {
    assert(::bind(sockfd_, bindAddr.addr(), sizeof(sockaddr_type)) == 0);
    backlog_ = backlog;
}

void ServerSocket::startListening() {
    assert(::listen(sockfd_, backlog_) == 0);
    closed_ = false;
    std::thread t(std::bind(&ServerSocket::workThread_, this));
    while(!isClosed()) {
        Socket* socket = accept();
        clientSockets_.push_back(socket);
    }
    t.join();
}

Socket* ServerSocket::accept() {
    socket_type clientSock;
    in_addr_type clientAddr;
    socketlen_type length = sizeof(clientAddr);
	clientSock = ::accept(sockfd_, (sockaddr_type*) &clientAddr, &length);
    Socket* socket = new Socket(clientSock, new InetAddress(clientAddr));
    if(onAccept) {
        onAccept(socket);
    }
    return socket;
}

void ServerSocket::close() {
#ifdef _WIN32
    ::closesocket(sockfd_);
#elif __linux__
    ::close(sockfd_);
#endif
    closed_ = true;
}

bool ServerSocket::isClosed() {
    return closed_;
}
#include<iostream>
void ServerSocket::workThread_() {
    int ret;
    timeval timeout;
    socket_type tmpfd;
    fd_set readFds;

    while(true) {
        FD_ZERO(&readFds);
        for(int i = 0; i < clientSockets_.size(); i++) {
            tmpfd = clientSockets_[i]->sockfd();
            FD_SET(tmpfd, &readFds);
        }
        timeout.tv_sec = 0;
        timeout.tv_usec = 0;
        ret = ::select(0, &readFds, nullptr, nullptr, &timeout);
        if(ret == 0) continue;
        for(int i = 0; i < clientSockets_.size(); i++) {
            if(FD_ISSET(clientSockets_[i]->sockfd(), &readFds)) {
                auto data = clientSockets_[i]->readAll();
                if(data.empty()) {
                    // closed
                    clientSockets_[i]->disconnect();
                    if(onClose) onClose(clientSockets_[i]);
                    clientSockets_.erase(clientSockets_.begin() + i);
                    i--;
                } else {
                    if(onMessage) onMessage(clientSockets_[i], std::move(data));
                }
            }
        }
    }
}